{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# User's Guide, Chapter 54: Extending Converter with New Formats"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For this example, rather than importing \\* from music21, we'll just import the modules we need.  If you're developing a new SubConverter class just for yourself you can import everything, but if you're thinking that you'd like to contribute your module back to music21 someday, it is important not to import \\* since that will create circular imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from music21 import converter, note, stream, meter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll create a dummy file format, '.sb' or the 'singlebeat' format which consists solely of a string of letters A-G in groups of any length separated by spaces. A-G represents the pitch name (no accidentals), while all notes written conjunctly in a group are interpreted as evenly fitting within a quarter note. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SingleBeat(converter.subConverters.SubConverter):\n",
    "    registerFormats = ('singlebeat',)  # note the comma after the string\n",
    "    registerInputExtensions = ('sb',)  # these are single element tuples.\n",
    "    \n",
    "    # we will just define parseData for now and let the SubConverter base class\n",
    "    # deal with loading data from files of type .sb and URLs ending in .sb for us.\n",
    "    \n",
    "    def parseData(self, strData, number=None):  # movement number is ignored...\n",
    "        '''  'AB C' -> A-8th, B-8th, C-qtr '''\n",
    "        strDataList = strData.split()\n",
    "        s = stream.Part()\n",
    "        m = meter.TimeSignature('4/4')\n",
    "        s.insert(0, m)\n",
    "        for beat in strDataList:\n",
    "            ql = 1.0/len(beat)\n",
    "            for n in beat:\n",
    "                nObj = note.Note(n)\n",
    "                nObj.duration.quarterLength = ql\n",
    "                s.append(nObj)\n",
    "        self.stream = s.makeMeasures()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we tell the converter module that our subconverter exists and can handle 'singlebeat'/'singleBeat'/'.sb' files. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "converter.registerSubconverter(SingleBeat)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now the format is ready to be used through converter.parse() on string data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0.0} <music21.stream.Measure 1 offset=0.0>\n",
      "    {0.0} <music21.clef.TrebleClef>\n",
      "    {0.0} <music21.meter.TimeSignature 4/4>\n",
      "    {0.0} <music21.note.Note C>\n",
      "    {0.3333} <music21.note.Note D>\n",
      "    {0.6667} <music21.note.Note C>\n",
      "    {1.0} <music21.note.Note D>\n",
      "    {1.5} <music21.note.Note E>\n",
      "    {2.0} <music21.note.Note F>\n",
      "    {3.0} <music21.note.Note G>\n",
      "    {3.25} <music21.note.Note A>\n",
      "    {3.5} <music21.note.Note G>\n",
      "    {3.75} <music21.note.Note B>\n",
      "{4.0} <music21.stream.Measure 2 offset=4.0>\n",
      "    {0.0} <music21.note.Note G>\n",
      "    {0.5} <music21.note.Note E>\n",
      "    {1.0} <music21.note.Note C>\n",
      "    {2.0} <music21.note.Note D>\n",
      "    {2.2} <music21.note.Note E>\n",
      "    {2.4} <music21.note.Note F>\n",
      "    {2.6} <music21.note.Note E>\n",
      "    {2.8} <music21.note.Note D>\n",
      "    {3.0} <music21.note.Note C>\n",
      "    {4.0} <music21.bar.Barline type=final>\n"
     ]
    }
   ],
   "source": [
    "s = converter.parse('CDC DE F GAGB GE C DEFED C', format='singleBeat')\n",
    "s.show('text')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or, singleBeat is now a custom header for parse:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<music21.note.Note G>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = converter.parse('singleBeat: CDC DE F GAGB GE C DEFED C')\n",
    "s[-1][0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or we can write out a file and read it in:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "tags": [
     "nbval-ignore-output"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/var/folders/1t/0_91h1r94kg21qt1jt20lxkc0000gn/T/music21/tmpvkxam6ik.sb\n"
     ]
    }
   ],
   "source": [
    "from music21 import environment\n",
    "e = environment.Environment()\n",
    "fp = e.getTempFile('.sb')\n",
    "with open(fp, 'w') as f:\n",
    "    f.write('CDC DE F GAGB GE C DEFED C')\n",
    "    \n",
    "print(fp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0.0} <music21.stream.Measure 1 offset=0.0>\n",
      "    {0.0} <music21.clef.TrebleClef>\n",
      "    {0.0} <music21.meter.TimeSignature 4/4>\n",
      "    {0.0} <music21.note.Note C>\n",
      "    {0.3333} <music21.note.Note D>\n",
      "    {0.6667} <music21.note.Note C>\n",
      "    {1.0} <music21.note.Note D>\n",
      "    {1.5} <music21.note.Note E>\n",
      "    {2.0} <music21.note.Note F>\n",
      "    {3.0} <music21.note.Note G>\n",
      "    {3.25} <music21.note.Note A>\n",
      "    {3.5} <music21.note.Note G>\n",
      "    {3.75} <music21.note.Note B>\n",
      "{4.0} <music21.stream.Measure 2 offset=4.0>\n",
      "    {0.0} <music21.note.Note G>\n",
      "    {0.5} <music21.note.Note E>\n",
      "    {1.0} <music21.note.Note C>\n",
      "    {2.0} <music21.note.Note D>\n",
      "    {2.2} <music21.note.Note E>\n",
      "    {2.4} <music21.note.Note F>\n",
      "    {2.6} <music21.note.Note E>\n",
      "    {2.8} <music21.note.Note D>\n",
      "    {3.0} <music21.note.Note C>\n",
      "    {4.0} <music21.bar.Barline type=final>\n"
     ]
    }
   ],
   "source": [
    "s2 = converter.parse(fp)\n",
    "s2.show('text')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you want to be extra-safe, pass the format in with the parse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<music21.stream.Part 0x119a786a0>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s3 = converter.parse(fp, format='singleBeat')\n",
    "s3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "SingleBeat will now appear in all places where fileformats are listed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('singlebeat', '.sb')"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from music21 import common\n",
    "common.findFormat('singleBeat')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can cleanup what we've done (always be a good citizen) by calling `converter.resetSubconverters`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "converter.resetSubconverters()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A demonstration of the singleBeat type (simplified a bit), but including a `.write()` call, can be found in the `converter` directory under the filename `qmConverter.py`."
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
